﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace _4Gewinnt
{
    /// <summary>
    /// A greedy engine: Dagobert moves to a column where he can maximize the number of consecutive tokens.
    /// If he senses that the opponent will win with his next move, he will move there.
    /// </summary>
    public class EngineDagobert : Engine
    {
        private Board board;
        private GameColor op;
        private Random R;
        private int ttw = 4;
        /// <summary>
        /// Using a proper RNG (System.Random is crap)
        /// </summary>
        private System.Security.Cryptography.RNGCryptoServiceProvider Rs;


        /// <summary>
        /// Greedy engine. Always places a token into a column to yield a maximum number of 
        /// consecutive tokens
        /// </summary>
        /// <param name="me"></param>
        public EngineDagobert(GameColor me)
        {
            this.board = new Board(ttw);
            this.R = new Random();
            this._me = me;
            if (_me == GameColor.Blue)
                op = GameColor.Red;
            else
                op = GameColor.Blue;
            this.Rs = new System.Security.Cryptography.RNGCryptoServiceProvider();

        }

        /// <summary>
        /// Search for a move that yields a maximum number of consecutive tokens
        /// </summary>
        /// <returns>The column number with the maximum number of consecutive tokens</returns>
        public override int makeMove()
        {
            int rows = board.Rows;
            int cols = board.Cols;

            //first move: into the middle
            if (board.isBoardFree())
            {
                board.moveTo(_me, 3);
                return 3;
            }

            //test if opponent wins with next move. If so, move there.
            for (int c = 0; c < cols; c++)
            {
                //test opponent's next moves
                Board nextMove = board.Clone();
                if (nextMove.isLegalColumn(c))
                {
                    nextMove.moveTo(op, c);

                    //if op wins when moving to column c, move there 
                    if (nextMove.hasWon(op))
                    {
                        board.moveTo(_me, c);
                        return c;
                    }
                }
            }

            //Compute greedy move
            //tiar stores for each column the number of tokens in a row I had when moving there
            int[] tiar = new int[cols];

            //stores the maximum number of tokens in a row I can gain this move
            int maxConsecToken = 0;

            //tryout each column: Search for column with maximum number of consecutive tokens when moving there
            for (int c = 0; c < cols; c++)
            {
                tiar[c] = tokensInARowInColumn(c);
                if (tiar[c] > maxConsecToken)
                    maxConsecToken = tiar[c];
            }

            //if there are more than one move with maximum number of tokens in a row: choose a random one
            int myMove = properRND();
            while (tiar[myMove] != maxConsecToken)
                myMove = properRND();

            //remember own move
            board.moveTo(_me, myMove);

            return myMove;
        }

        /// <summary>
        /// Gets random number between 0 and 6 using a proper random number generator
        /// </summary>
        /// <returns>A random number between 0 and 6</returns>
        private int properRND()
        {
            byte[] rnd = new byte[1];
            Rs.GetBytes(rnd);
            return ((int)rnd[0]) % 7;

        }


        /// <summary>
        /// Computes the number of token in a row (horizontally, vertically and diagonally) when moving own stone here
        /// </summary>
        /// <param name="c">The column</param>
        /// <returns>The number of tokens I have in a row if moving to column c</returns>
        private int tokensInARowInColumn(int c)
        {
            //find accoring row number
            int r = board.findLegalRowInColumn(c);

            //if c is illegal column:
            if (r == -1)
                return -1;

            //count stores the maximum number of consecutive tokens in this column
            int count = 0;
            int a = 0;
            int b = 0;

            //search vertical: |
            int countVert = 0;
            a = r - 1;
            b = c;
            while (isMyToken(a, b))
            {
                countVert++;
                a--;
            }
            a = r + 1;
            b = c;
            while (isMyToken(a, b))
            {
                countVert++;
                a++;
            }
            if (countVert > count)
                count = countVert;

            //search horizontal: -
            int countHor = 0;
            a = r;
            b = c - 1;
            while (isMyToken(a, b))
            {
                countHor++;
                b--;
            }
            a = r;
            b = c + 1;
            while (isMyToken(a, b))
            {
                countHor++;
                b++;
            }
            if (countHor > count)
                count = countHor;

            //search diagonal1: /
            int countDiag1 = 0;
            a = r + 1;
            b = c - 1;
            while (isMyToken(a, b))
            {
                countDiag1++;
                a++;
                b--;
            }
            a = r - 1;
            b = c + 1;
            while (isMyToken(a, b))
            {
                countDiag1++;
                a--;
                b++;
            }
            if (countDiag1 > count)
                count = countDiag1;

            //search diagonal2: \
            int countDiag2 = 0;
            a = r + 1;
            b = c + 1;
            while (isMyToken(a, b))
            {
                countDiag2++;
                a++;
                b++;
            }
            a = r - 1;
            b = c - 1;
            while (isMyToken(a, b))
            {
                countDiag2++;
                a--;
                b--;
            }
            if (countDiag2 > count)
                count = countDiag2;

            return count;
        }

        /// <summary>
        /// Checks whether my token is on (r,c). 
        /// </summary>
        /// <param name="r">The row</param>
        /// <param name="c">The column</param>
        /// <returns>True, if my token is on (r,c). False, if (r,c) is outside the grid. </returns>
        private bool isMyToken(int r, int c)
        {
            if (r >= 0 && r < board.Rows && c >= 0 && c < board.Cols)
            {
                if (board.playerAt(r, c) == _me)
                    return true;
            }
            return false;
        }


        /// <summary>
        /// Learns an opponent's move
        /// </summary>
        /// <param name="c">The column the opponent moves to</param>
        public override void update(int c)
        {
            board.moveTo(op, c);
        }

        /// <summary>
        /// Called after each engine's turn. null means "nothing to say"
        /// </summary>
        /// <returns>A message a engine can send after each own turn</returns>
        public override string say()
        {
            return null;
        }

        /// <summary>
        /// Return Engine Name
        /// </summary>
        /// <returns></returns>
        public override string ToString()
        {
            return "Dagobert";
        }

    }
}
